package com.uuah.server.lifecycle;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.functors.InstanceofPredicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.exception.AbstractUuahException;
import com.uuah.exception.lifecycle.LifecycleException;
import com.uuah.server.UuahServer;
import com.uuah.server.api.lifecycle.ILifecyclePhase;
import com.uuah.server.config.i18n.ServerMessages;
import com.uuah.utils.ClassUtils;
import com.uuah.utils.CollectionUtils;

/**
 *
 * TODO comment ...
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 下午01:52:34
 * @version 1.0.0
 */
public class LifecyclePhase implements ILifecyclePhase {
	protected transient final Logger logger = LoggerFactory
			.getLogger(LifecyclePhase.class);
	private Class lifecycleClass;
	private Method lifecycleMethod;
	private Set orderedLifecycleObjects = new LinkedHashSet(6);
	private Class[] ignorredObjectTypes;
	private String name;
	private String oppositeLifecyclePhase;
	private Set supportedPhases;

	public LifecyclePhase(String name, Class lifecycleClass,
			String oppositeLifecyclePhase) {
		this.name = name;
		this.lifecycleClass = lifecycleClass;
		lifecycleMethod = lifecycleClass.getMethods()[0];
		this.oppositeLifecyclePhase = oppositeLifecyclePhase;
	}

	public void applyLifecycle(Collection objects, String currentPhase)
			throws AbstractUuahException {
		if (logger.isDebugEnabled()) {
			logger.debug("Attempting to apply lifecycle phase: " + getName());
		}

		Set duplicates = new HashSet();

		for (Iterator iterator = orderedLifecycleObjects.iterator(); iterator
				.hasNext();) {
			LifecycleObject lo = (LifecycleObject) iterator.next();

			List targets = new LinkedList(CollectionUtils.select(objects,
					new InstanceofPredicate(lo.getType())));
			if (targets.size() == 0) {
				continue;
			}

			lo.firePreNotification(UuahServer.getUuahContext());

			for (Iterator target = targets.iterator(); target.hasNext();) {
				Object o = target.next();
				if (duplicates.contains(o)) {
					target.remove();
				} else {
					if (logger.isDebugEnabled()) {
						logger.debug("lifecycle phase: " + getName()
								+ " for object: " + o);
					}
					this.applyLifecycle(o);
					target.remove();
					duplicates.add(o);
				}
			}

			lo.firePostNotification(UuahServer.getUuahContext());
		}
	}

	protected List sortLifecycleInstances(Collection objects, LifecycleObject lo) {
		return new ArrayList(objects);
	}

	public void addOrderedLifecycleObject(LifecycleObject lco) {
		orderedLifecycleObjects.add(lco);
	}

	public void removeOrderedLifecycleObject(LifecycleObject lco) {
		orderedLifecycleObjects.remove(lco);
	}

	protected boolean ignoreType(Class type) {
		if (ignorredObjectTypes == null) {
			return false;
		} else {
			for (int i = 0; i < ignorredObjectTypes.length; i++) {
				Class ignorredObjectType = ignorredObjectTypes[i];
				if (ignorredObjectType.isAssignableFrom(type)) {
					return true;
				}
			}
		}
		return false;
	}

	public Set getOrderedLifecycleObjects() {
		return orderedLifecycleObjects;
	}

	public void setOrderedLifecycleObjects(Set orderedLifecycleObjects) {
		this.orderedLifecycleObjects = orderedLifecycleObjects;
	}

	public Class[] getIgnoredObjectTypes() {
		return ignorredObjectTypes;
	}

	public void setIgnoredObjectTypes(Class[] ignorredObjectTypes) {
		this.ignorredObjectTypes = ignorredObjectTypes;
	}

	public Class getLifecycleClass() {
		return lifecycleClass;
	}

	public void setLifecycleClass(Class lifecycleClass) {
		this.lifecycleClass = lifecycleClass;
	}

	public String getName() {
		return name;
	}

	public Set getSupportedPhases() {
		return supportedPhases;
	}

	public void setSupportedPhases(Set supportedPhases) {
		this.supportedPhases = supportedPhases;
	}

	public void registerSupportedPhase(String phase) {
		if (supportedPhases == null) {
			supportedPhases = new HashSet();
		}
		supportedPhases.add(phase);
	}

	public boolean isPhaseSupported(String phase) {
		if (getSupportedPhases() == null) {
			return true;
		} else {
			if (getSupportedPhases().contains(ALL_PHASES)) {
				return true;
			} else {
				return getSupportedPhases().contains(phase);
			}
		}
	}

	public void applyLifecycle(Object o) throws LifecycleException {
		if (o == null) {
			return;
		}
		if (ignoreType(o.getClass())) {
			return;
		}
		if (!getLifecycleClass().isAssignableFrom(o.getClass())) {
			return;
		}
		try {
			lifecycleMethod.invoke(o, ClassUtils.NO_ARGS);
		} catch (Exception e) {
			throw new LifecycleException(ServerMessages
					.failedToInvokeLifecycle(lifecycleMethod.getName(), o), e,
					this);
		}
	}

	public String getOppositeLifecyclePhase() {
		return oppositeLifecyclePhase;
	}
}
